// Unity C# reference source
// Copyright (c) Unity Technologies. For terms of use, see
// https://unity3d.com/legal/licenses/Unity_Reference_Only_License

using System;
using System.Collections.Generic;
using System.Collections;
using System.Linq;
using UnityEditor.AnimatedValues;
using UnityEditorInternal;
using UnityEngine;
using UnityEngineInternal;
using Object = UnityEngine.Object;
using System.Globalization;

namespace UnityEditor
{
    internal class LightingWindowLightmapPreviewTab : LightingWindow.WindowTab
    {
        LightmapType m_LightmapType         = LightmapType.NoLightmap;
        Vector2 m_ScrollPosition            = Vector2.zero;
        bool m_ShouldScrollToLightmapIndex  = false;

        int m_ActiveGameObjectLightmapIndex     = -1; // the object the user selects in the scene
        int m_SelectedLightmapIndex             = 0;

        SavedBool m_ShowBakedLightmaps = new SavedBool("LightingWindow.ShowBakedLightmaps", true);
        SavedBool m_ShowPreviewLightmaps = new SavedBool("LightingWindow.ShowPreviewLightmaps", true);

        // realtime lightmaps
        Hash128 m_ActiveGameObjectTextureHash   = new Hash128(); // the object the user selects in the scene

        SerializedObject m_LightmapSettings;

        static class Styles
        {
            public static readonly GUIStyle SelectedLightmapHighlight = "LightmapEditorSelectedHighlight";
            public static readonly GUIContent LightingDataAsset = EditorGUIUtility.TrTextContent("Lighting Data Asset", "A different LightingData.asset can be assigned here. These assets are generated by baking a scene in the OnDemand mode.");
            public static readonly GUIContent OpenPreview = EditorGUIUtility.TrTextContent("View");

            public static readonly GUIStyle OpenPreviewStyle = EditorStyles.objectFieldThumb.name + "LightmapPreviewOverlay";

            public static readonly GUIContent BakedLightmapsFoldoutTitle = EditorGUIUtility.TrTextContent("Baked Lightmaps");
            public static readonly GUIContent PreviewLightmapsFoldoutTitle = EditorGUIUtility.TrTextContent("Preview Lightmaps");
        }

        public LightingWindowLightmapPreviewTab(LightmapType type)
        {
            m_LightmapType = type;
        }

        private bool isRealtimeLightmap
        {
            get
            {
                return m_LightmapType == LightmapType.DynamicLightmap;
            }
        }

        private bool showDebugInfo
        {
            get
            {
                return !isRealtimeLightmap && Unsupported.IsDeveloperMode();
            }
        }

        public void OnSelectionChange()
        {
            MeshRenderer renderer;
            Terrain terrain = null;

            // if the active object in the selection is a renderer or a terrain, we're interested in it's lightmapIndex
            if (Selection.activeGameObject == null ||
                ((renderer = Selection.activeGameObject.GetComponent<MeshRenderer>()) == null &&
                 (terrain = Selection.activeGameObject.GetComponent<Terrain>()) == null))
            {
                m_ActiveGameObjectLightmapIndex = -1;
                m_ActiveGameObjectTextureHash = new Hash128();
                return;
            }
            if (isRealtimeLightmap)
            {
                Hash128 inputSystemHash;
                if ((renderer != null && Lightmapping.GetInputSystemHash(renderer.GetInstanceID(), out inputSystemHash))
                    || (terrain != null && Lightmapping.GetInputSystemHash(terrain.GetInstanceID(), out inputSystemHash)))
                {
                    m_ActiveGameObjectTextureHash = inputSystemHash;
                }
                else
                    m_ActiveGameObjectTextureHash = new Hash128();
            }
            else
                m_ActiveGameObjectLightmapIndex = renderer != null ? renderer.lightmapIndex : terrain.lightmapIndex;

            m_ShouldScrollToLightmapIndex = true;
        }

        public void OnEnable()
        {
            InitSettings();
        }

        public void OnDisable()
        {
        }

        public void OnGUI()
        {
            InitSettings();

            m_LightmapSettings.Update();

            m_ScrollPosition = GUILayout.BeginScrollView(m_ScrollPosition);

            if (!isRealtimeLightmap)
            {
                EditorGUI.BeginChangeCheck();
                var lda = (LightingDataAsset)EditorGUILayout.ObjectField(Styles.LightingDataAsset, Lightmapping.lightingDataAsset, typeof(LightingDataAsset), true);
                if (EditorGUI.EndChangeCheck())
                    Lightmapping.lightingDataAsset = lda;
                GUILayout.Space(10);
            }
            else
            {
                GUILayout.Space(30);
            }

            DebugInfoSection();

            if (isRealtimeLightmap)
            {
                VisualisationGITexture[] realtimeLightmaps = LightmapVisualizationUtility.GetRealtimeGITextures(GITextureType.Irradiance);
                LightmapListGUI(realtimeLightmaps, lm => lm.texture, lm => lm.hash, false, false);
            }
            else
            {
                var interactiveLightmaps = InteractiveLightBaking.GetLightmapData();
                if (interactiveLightmaps.numLightmaps > 0 && Lightmapping.shouldBakeInteractively) // Hide preview lightmaps when not in preview mode
                {
                    m_ShowPreviewLightmaps.value = EditorGUILayout.Foldout(m_ShowPreviewLightmaps.value, Styles.PreviewLightmapsFoldoutTitle);

                    if (m_ShowPreviewLightmaps)
                    {
                        LightmapListGUI(interactiveLightmaps.lightmaps, lm => lm, lm => new Hash128(), true, true);
                    }
                }

                LightmapData[] lightmaps = LightmapSettings.lightmaps;
                if (lightmaps.Length > 0)
                {
                    m_ShowBakedLightmaps.value = EditorGUILayout.Foldout(m_ShowBakedLightmaps.value, Styles.BakedLightmapsFoldoutTitle);

                    if (m_ShowBakedLightmaps)
                    {
                        LightmapListGUI(lightmaps, lm => lm.lightmapColor, lm => new Hash128(), false, true);
                    }
                }
            }

            GUILayout.EndScrollView();
        }

        public void OnSummaryGUI()
        {
            LightingWindow.Summary();
        }

        public bool HasHelpGUI()
        {
            return false;
        }

        private void InitSettings()
        {
            if (m_LightmapSettings == null || m_LightmapSettings.targetObject == null || m_LightmapSettings.targetObject != LightmapEditorSettings.GetLightmapSettings())
            {
                m_LightmapSettings = new SerializedObject(LightmapEditorSettings.GetLightmapSettings());
            }
        }

        private void LightmapListGUI<T>(T[] lightmaps, Func<T, Texture2D> getTexture, Func<T, Hash128> getHash, bool useInteractiveLightBakingData, bool showCompressionDetails)
        {
            int length = lightmaps.Length;

            if (m_SelectedLightmapIndex >= length)
                m_SelectedLightmapIndex = 0;

            for (int i = 0; i < length; i++)
            {
                Texture2D texture = getTexture(lightmaps[i]);

                if (texture != null)
                {
                    GUILayout.BeginHorizontal();
                    GUILayout.Space(10);

                    Hash128 hash = getHash(lightmaps[i]);

                    LightmapField(texture, i, hash, useInteractiveLightBakingData);

                    GUILayout.Space(5);
                    GUILayout.BeginVertical();
                    GUILayout.Label("Index: " + i, EditorStyles.miniBoldLabel);
                    GUILayout.Label("Size: " + texture.width + "x" + texture.height, EditorStyles.miniBoldLabel);

                    if (showCompressionDetails)
                    {
                        GUILayout.Label("Format: " + texture.format + (Lightmapping.GetLightingSettingsOrDefaultsFallback().lightmapCompression == LightmapCompression.None ? " (uncompressed)" : " (compressed)"), EditorStyles.miniBoldLabel);
                    }
                    else
                    {
                        GUILayout.Label("Format: " + texture.format, EditorStyles.miniBoldLabel);
                    }

                    GUILayout.EndVertical();
                    GUILayout.FlexibleSpace();
                    GUILayout.EndHorizontal();
                    GUILayout.Space(5);
                }
            }
        }

        private void LightmapField(Texture2D lightmap, int index, Hash128 hash, bool useInteractiveLightBakingData)
        {
            Rect rect = GUILayoutUtility.GetRect(100, 100, EditorStyles.objectField);
            Rect buttonRect = new Rect(rect.xMax - 35, rect.yMax - 14, 35, 14);

            if (EditorGUI.Toggle(rect, index == m_SelectedLightmapIndex, EditorStyles.objectFieldThumb))
            {
                m_SelectedLightmapIndex = index;

                if ((buttonRect.Contains(Event.current.mousePosition) && Event.current.clickCount == 1) ||
                    (rect.Contains(Event.current.mousePosition) && Event.current.clickCount == 2))
                {
                    if (isRealtimeLightmap)
                        LightmapPreviewWindow.CreateLightmapPreviewWindow(m_SelectedLightmapIndex, true, true, useInteractiveLightBakingData);
                    else
                        LightmapPreviewWindow.CreateLightmapPreviewWindow(m_SelectedLightmapIndex, false, true, useInteractiveLightBakingData);
                }
                else if (rect.Contains(Event.current.mousePosition) && Event.current.clickCount == 1)
                {
                    Object actualTargetObject = lightmap;
                    Component com = actualTargetObject as Component;
                    if (com)
                        actualTargetObject = com.gameObject;
                    EditorGUI.PingObjectOrShowPreviewOnClick(actualTargetObject, GUILayoutUtility.GetLastRect());
                }
            }

            if (Event.current.type == EventType.Repaint)
            {
                rect = EditorStyles.objectFieldThumb.padding.Remove(rect);
                EditorGUI.DrawPreviewTexture(rect, lightmap);

                Styles.OpenPreviewStyle.Draw(rect, Styles.OpenPreview, false, false, false, false);

                if ((!isRealtimeLightmap && index == m_ActiveGameObjectLightmapIndex) || (isRealtimeLightmap && hash == m_ActiveGameObjectTextureHash))
                {
                    Styles.SelectedLightmapHighlight.Draw(rect, false, false, false, false);

                    if (m_ShouldScrollToLightmapIndex)
                    {
                        GUI.ScrollTo(rect);

                        m_ShouldScrollToLightmapIndex = false;
                        GUIView.current.Repaint();
                    }
                }
            }
        }

        //***** DEBUG DATA *****//

        const string kEditorPrefsTransmissionTextures = "LightingWindowGlobalMapsTT";
        const string kEditorPrefsGeometryData = "LightingWindowGlobalMapsGD";
        const string kEditorPrefsInFlight = "LightingWindowGlobalMapsIF";
        const string kEditorPrefsMaterialTextures = "LightingWindowGlobalMapsMT";
        const string kEditorPrefsLightProbes = "LightingWindowGlobalLightProbes";

        private static string SizeString(float size)
        {
            if (size < 0)
                return "unknown";
            float val = size;
            string[] scale = new string[] { "TB", "GB", "MB", "KB", "Bytes" };
            int idx = scale.Length - 1;
            while (val > 1000.0f && idx >= 0)
            {
                val /= 1000f;
                idx--;
            }

            if (idx < 0)
                return "<error>";

            return string.Format("{0:0.##} {1}", val, scale[idx]);
        }

        private void DebugInfoSection()
        {
            if (!showDebugInfo)
                return;

            float oldWidth = EditorGUIUtility.labelWidth;
            EditorGUIUtility.labelWidth = 400.0f;

            {
                float gpuMemory = Lightmapping.ComputeTotalGPUMemoryUsageInBytes();
                if (gpuMemory > 0.0f)
                {
                    string foldoutGPUMemoryGPU = String.Format("Total GPU memory ({0})", SizeString(gpuMemory));
                    EditorGUILayout.FoldoutTitlebar(false, new GUIContent(foldoutGPUMemoryGPU), true);
                    if (GUILayout.Button("Dump detail to Editor.log", EditorStyles.miniButton, GUILayout.ExpandWidth(false)))
                    {
                        Lightmapping.LogGPUMemoryStatistics();
                    }
                }
            }

            EditorGUILayout.Space();
            EditorGUIUtility.labelWidth = oldWidth;
        }
    }
} // namespace
